package com.ld.security.common.mybatis.base.service;

import com.ld.security.common.core.util.SpringUtil;
import com.ld.security.common.mybatis.anno.ManyToMany;
import com.ld.security.common.mybatis.anno.ManyToOne;
import com.ld.security.common.mybatis.anno.OneToMany;
import com.ld.security.common.mybatis.anno.OneToOne;
import com.ld.security.common.core.base.column.ListString;
import com.ld.security.common.mybatis.constant.SqlCondition;
import com.ld.security.common.core.exception.ApiException;
import com.ld.security.common.core.util.GenericUtil;
import com.ld.security.common.core.util.ReflectionUtil;
import com.ld.security.common.mybatis.base.entity.SuperEntity;
import com.ld.security.common.mybatis.base.mapping.SuperMapping;
import com.ld.security.common.mybatis.core.wrapper.EntityWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Field;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class ProxyService {

    @Autowired(required = false)
    private List<SuperMapping> superMappingList;

    String cont_id="id";

    private SuperMapping getService(Field field){

        if(superMappingList == null){
            ApiException.throw400("异常superMappingList");
        }
        Class clazz= null;
        if(List.class.isAssignableFrom(field.getType())){
            clazz =  GenericUtil.getGeneric(field,0);
        }else{
            clazz = field.getType();
        }
        for (SuperMapping superMapping : superMappingList) {
            if(clazz.isAssignableFrom( superMapping.getEntityClass())){
                return superMapping;
            }
        }
        ApiException.throw400("异常实体类:"+clazz);
        return null;
    }







        public List<?> initOneToOne(Class<?> clazz, List<?> list){
        for (Field field : ReflectionUtil.getAllDeclaredFields(clazz)) {
            OneToOne oneToOne =  AnnotationUtils.findAnnotation(field,OneToOne.class);
            if(oneToOne ==null ){
                continue;
            }
            List<Object> ids =   list.stream().map(val -> {
                Object v =  ReflectionUtil.invokeGetter(val,oneToOne.value());
                return v;
            }).collect(Collectors.toList());
            SuperMapping superMapping =   getService(field);
            EntityWrapper ew =  new EntityWrapper<>().addField(oneToOne.key(),SqlCondition.in,ids);

            List<SuperEntity> superEntities =  superMapping.all(ew);

            invokeSetter(list,oneToOne.value(),oneToOne.key(), field.getName(),superEntities);

        }

        return list;
    }


    public List<?> initManyToOne(Class<?> clazz, List<?> list){
        for (Field field : ReflectionUtil.getAllDeclaredFields(clazz)) {

            ManyToOne manyToOne =  AnnotationUtils.findAnnotation(field,ManyToOne.class);

            if(manyToOne ==null){
                continue;
            }
            List<Object> ids =   list.stream().flatMap(val -> {
                Collection<?> cols = this.invokeGetter(val,manyToOne.value());
                return cols.stream();
            }).collect(Collectors.toList());

            SuperMapping superMapping =   getService( field);
            EntityWrapper ew =  new EntityWrapper<>().addField(manyToOne.key(),SqlCondition.in,ids);

            List<SuperEntity> superEntities =  superMapping.all(ew);

            invokeSetter(list,manyToOne.value(),manyToOne.key(),  field.getName(),superEntities);

        }

        return list;
    }



    public List<?> initOneToMany(Class<?> clazz, List<?> list){
        for (Field field : ReflectionUtil.getAllDeclaredFields(clazz)) {

            OneToMany oneToMany =  AnnotationUtils.findAnnotation(field,OneToMany.class);

            if(oneToMany ==null){
                continue;
            }
            List<Object> ids =   list.stream().flatMap(val -> {
                Collection<?> o = this.invokeGetter(val,oneToMany.value());
                return  o.stream();
            }).collect(Collectors.toList());

            SuperMapping superMapping =   getService(field);
            EntityWrapper ew =  new EntityWrapper<>().addField(oneToMany.key(),SqlCondition.in,ids);

            List<SuperEntity> superEntities =  superMapping.all(ew);

            invokeSetter(list,oneToMany.value(), oneToMany.key(), field.getName(),superEntities);


        }

        return list;
    }

        public List<?> initMapping(Class<?> clazz, List<?> list) {
        this.initOneToOne(clazz,list);
        this.initManyToOne(clazz,list);
        this.initOneToMany(clazz,list);
        this.initManyToMany(clazz,list);
        return list;
    }


    /**
     * 在执行过程中,发现了泛型集合，则递归
     * 基本数据，则返回
     * @param e
     * @return
     */
    private Collection<?> invokeGetter(Object e,    List<String> keys ){

        LinkedList linkedList =  new LinkedList<>();
        if(keys.size() > 1){

            //递归情况
            String key = keys.remove(0);
            Object v =  ReflectionUtil.invokeGetter(e,key);
            if(v instanceof Collection){
                Collection<?> vs = (Collection)v;
                for (Object v2 : vs) {

                    Collection list =  invokeGetter(v2,keys);
                    linkedList.addAll(list);
                }
            }else{
                Collection list =  invokeGetter(v,keys);
                linkedList.addAll(list);
            }


        }else{
            Object v =   ReflectionUtil.invokeGetter(e,keys.get(0));
            if(v instanceof Collection){
                Collection collection =(Collection) v;
                linkedList.addAll(collection);
            }else{
                linkedList.add(v);
            }
        }

        return  linkedList;
    }

    private void  invokeSetter(Object obj,String key,Object val){
        Field field =  ReflectionUtils.findField(obj.getClass(),key);
        if(List.class.isAssignableFrom(field.getType())){
            List list =  ReflectionUtil.invokeGetter(obj,key);
            if(list == null){
                list = new LinkedList();
                ReflectionUtil.invokeSetter(obj,key,list);
            }
            list.add(val);
        } else{
            ReflectionUtil.invokeSetter(obj,key,val);
        }
    }

    private void  invokeSetter(List<?> list,String value,
                               String key,
                               String fieldName,
                               List<? extends SuperEntity> superEntities){
        for (Object e : list) {

            Collection<?> ys = invokeGetter(e,value);
            for (Object y : ys) {
                for (SuperEntity superEntity : superEntities) {
                    Object x = ReflectionUtil.invokeGetter(superEntity,key);
                    if(x.equals(y)){
                        invokeSetter(e,fieldName,superEntity);
                    }

                }
            }

        }
    }


    private Collection<?> invokeGetter(Object e,    String string ) {
        String[] keys =string.split(ReflectionUtil.sp_prefix);
        ListString listString =   new ListString(keys);
        return this.invokeGetter(e,listString);
    }

        public List<?> initManyToMany(Class<?> clazz, List<?> list){
        for (Field field : ReflectionUtil.getAllDeclaredFields(clazz)) {

            ManyToMany manyToMany =  AnnotationUtils.findAnnotation(field,ManyToMany.class);

            if(manyToMany ==null){
                continue;
            }
            List<Object> ids =   list.stream().flatMap(val -> {

                return this.invokeGetter(val,manyToMany.value()).stream();
            }).collect(Collectors.toList());

            SuperMapping superMapping =   getService( field);
            EntityWrapper ew =  new EntityWrapper<>().addField(manyToMany.key(),SqlCondition.in,ids);

            List<SuperEntity> superEntities =  superMapping.all(ew);

            invokeSetter(list,manyToMany.value(),manyToMany.key(), field.getName(),superEntities);


        }

        return list;
    }
}
